﻿using System;
using System.Web;

using System.Drawing;
using System.Drawing.Imaging;
using System.Web.SessionState;

namespace IFD2
{
    public class img_chk : IHttpHandler, IRequiresSessionState
    {

        public void ProcessRequest(HttpContext context)
        {
            //context.Response.ContentType = "text/plain";
            //context.Response.Write("Hello World");
            string strKey = "";
            byte[] data = GenerateVerifyImage(4, ref strKey);
            context.Session["CHK_CODE"] = strKey.ToLower();

            //Response.ContentType = "image/vnd.wap.wbmp";
            context.Response.ContentType = "image/png";

            //Response.OutputStream.Write(data, 0, data.Length);
            context.Response.BinaryWrite(data);
            context.Response.End();
        }

        private const double PI = 3.1415926535897932384626433832795;
        private const double PI2 = 6.283185307179586476925286766559;

        private byte[] GenerateVerifyImage(int nLen, ref string strKey)
        {
            /// <summary>
            /// 生成图片验证码
            /// </summary>
            /// <param name="nLen">验证码的长度</param>
            /// <param name="strKey">输出参数，验证码的内容</param>
            /// <returns>图片字节流</returns>
            int nCharWidth = 16;
            int nBmpWidth = nCharWidth * nLen + 10;
            int nBmpHeight = 30;// 25;
            System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(nBmpWidth, nBmpHeight);
            System.Random rd = new System.Random((int)System.DateTime.Now.Ticks);
            int i, j;
            string strResult = "";

            //  填充位图背景
            System.Drawing.Graphics graph = System.Drawing.Graphics.FromImage(bmp);
            graph.FillRectangle(new SolidBrush(System.Drawing.Color.FromArgb(255, 255, 255))//(238, 238, 238)
             , 0
             , 0
             , nBmpWidth
             , nBmpHeight);


            //  绘制干扰线条:每个字符一条干扰线
            int nLines = nLen;
            int x1, y1, x2, y2;
            System.Drawing.Pen pen;// = new System.Drawing.Pen(System.Drawing.Color.FromArgb(50, 50, 205), 2);
            for (int a = 0; a < nLines; a++)
            {
                pen = new System.Drawing.Pen(System.Drawing.Color.FromArgb(0, 0, 0), rd.Next(1, 3));
                //x1 = rd.Next(nBmpWidth);
                x1 = rd.Next(a * nCharWidth, a * nCharWidth + 6);
                y1 = rd.Next(nBmpHeight - 2);
                //x2 = x1 + rd.Next(-1 * nBmpWidth, nBmpWidth);//rd.Next(-30,30);
                x2 = x1 + nCharWidth + rd.Next(5);
                //y2 = y1 + rd.Next(-1 * nBmpHeight, nBmpHeight);//rd.Next(-30,30);
                y2 = rd.Next(nBmpHeight - 2);
                graph.DrawLine(pen, x1, y1, x2, y2);
            }

            // 循环取得字符，并绘制
            // 采用的字符集，可以扩展，并可以控制字符出现的几率
            string strCode = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz123456789";
            int x, y;
            bool bold;
            for (i = 0; i < nLen; i++)
            {
                x = (i * nCharWidth + rd.Next(3));
                y = rd.Next(1, 6);
                if (rd.Next(0, 1) > 0)
                    bold = true;
                else
                    bold = false;
                // 字体:Courier New特征点更多，比Arial更易识别，电脑也更易识别？
                System.Drawing.Font font;
                if (bold)
                    font = new System.Drawing.Font("Arial",
                    rd.Next(14, 16),
                    System.Drawing.FontStyle.Bold);
                else
                    font = new System.Drawing.Font("Arial",
                    rd.Next(14, 16));
                char c = strCode[rd.Next(strCode.Length)];  // 随机获取字符
                strResult += c.ToString();

                // 绘制字符
                graph.DrawString(c.ToString(),
                 font,
                 new SolidBrush(System.Drawing.Color.FromArgb(0, 0, 0)),
                 x,
                 y);
            }

            // 扭曲图片.不扭曲容易编程遍历匹配测试找出对应字母！
            System.Drawing.Bitmap bmp2 = new System.Drawing.Bitmap(nBmpWidth, nBmpHeight);
            graph = System.Drawing.Graphics.FromImage(bmp2);
            graph.FillRectangle(new SolidBrush(System.Drawing.Color.FromArgb(255, 255, 255))//同前面背景色！
             , 0
             , 0
             , nBmpWidth
             , nBmpHeight);
            int amplitude, phase;
            amplitude = rd.Next(2, 4);//扭曲幅度:字符一般3左右
            phase = rd.Next(0, 7);//初始相位：0-2*PI
            for (i = 0; i < bmp.Width; i++)
            {
                for (j = 0; j < bmp.Height; j++)
                {
                    double dx = 0;
                    dx = (PI2 * (double)j) / bmp.Height;
                    dx += phase;
                    double dy = Math.Sin(dx);
                    // 取得当前点的颜色
                    int nOldX = 0, nOldY = 0;
                    nOldX = i + (int)(dy * amplitude);
                    nOldY = j;
                    System.Drawing.Color color = bmp.GetPixel(i, j);
                    if (nOldX >= 0 && nOldX < bmp2.Width
                     && nOldY >= 0 && nOldY < bmp2.Height)
                    {
                        bmp2.SetPixel(nOldX, nOldY, color);
                    }
                }
            }


            // 4. 输出字节流
            System.IO.MemoryStream bstream = new System.IO.MemoryStream();
            bmp2.Save(bstream, System.Drawing.Imaging.ImageFormat.Png);
            bmp2.Dispose();
            graph.Dispose();
            strKey = strResult;
            byte[] byteReturn = bstream.ToArray();
            bstream.Close();
            return byteReturn;

            // 4. 输出字节流WBMP
            //byte[] byteReturn = GetWbmp(bmp, nBmpWidth, nBmpHeight);
            //return byteReturn;

        }

        private byte[] GetWbmp(Bitmap bmpImage, int intWX, int intWY)
        {
            //应用：GetWbmp(bitmap文件,图像宽度,图像高度)，所获取的byte数据即为wbmp流，具体应用如下：
            //byte[]byteWbmp=GetWbmp(bitImage,intX,intY);
            //...
            //Response.ContentType="image/vnd.wap.wbmp";
            //Response.BinaryWrite(byteWbmp);

            //WBMP的格式如下(例如Width=16,Height=15的一个白色的画面)： 
            //0x00,0x00,0x10,0x0f,0xff,0xff......,0xff。 
            //头两个字节为0，现在还不清楚它的用途。 
            //第三个字节为Width,第四个字节为Height,后面的是数据。 
            //数据有以下特点：用一个Bit对应一个Pixel。0表示黑色，1表示白色。
            //高位开始到底位结束对应着从左到右，数据从低地址到高地址对应着从上到下。
            //一个字节可以表示8个像素，不足的部分可以用0补齐。

            int intWXl = (intWX + 7) / 8;
            int intWImgl = intWXl * intWY + 4;
            byte[] byteWImg = new byte[intWImgl];
            byteWImg[2] = (byte)intWX;
            byteWImg[3] = (byte)intWY;
            byte byteTmp = 0;
            int k = 4;//wbmp文件数据指针 
            int l = 0;//位->字节计数器
            int m = 7;//移位器：从高位到低位
            for (int i = 0; i < intWY; i++)
            {
                for (int j = 0; j < intWX; j++)
                {
                    Color c = bmpImage.GetPixel(j, i);
                    //if (c.R == 255) 
                    if ((c.R * 0.299) + (c.G * 0.587) + (c.B * 0.114) >= 128)//判断黑白！
                        byteTmp = (byte)(byteTmp + Math.Pow(2, m));
                    l++;
                    m--;
                    if (l > 7 || j == intWX - 1)
                    {
                        byteWImg[k] = byteTmp;
                        byteTmp = 0;
                        k++;
                        l = 0;
                        m = 7;
                    }

                }
            }
            return byteWImg;
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }

    }
}